package in.ac.iitb.cse.nlp.postagger.data;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedSet;
import java.util.TreeSet;

//import sun.org.mozilla.javascript.WrapFactory;

//import com.panayotis.gnuplot.JavaPlot;

/**
 * 
 * 
 * This class will hold all the ds and methods to deal with confusion matrix
 * 
 */
public class ConfusionMatrix implements Matrix {
	/**
	 * confusion to be put into this
	 */
	private Map<Tag, HashMap<Tag, Integer>> confusionMatrix;
	// private LinkedHashSet<String> tags = new LinkedHashSet<String>();
	private SortedSet<String> tags = new TreeSet<String>();

	public ConfusionMatrix() {
		this.confusionMatrix = new HashMap<Tag, HashMap<Tag, Integer>>();
	}

	public int getTruePosCount(String tag) {
		int count = 0;
		count = getCount(tag, tag);
		return count;
	}

	public int getTruePosFalsePosCount(String predicted) {
		int total = 0;
		for (String tag : tags) {
			total += getCount(tag, predicted);
		}
		return total;
	}

	public int getTruePosFalseNegCount(String original) {
		int total = 0;
		if (this.confusionMatrix.containsKey(new Tag(original))) {
			for (Entry<Tag, Integer> cell : confusionMatrix.get(
					new Tag(original)).entrySet()) {
				total += cell.getValue();
			}
		}
		return total;
	}

	/**
	 * this method will update the count of the cell by one. If the ow column
	 * not present creates one
	 */
	
	@Override
	public void update(String original, String predicted) {
		// Dont create a new entry if the corresponding cell is not there
		tags.add(original);
		Tag originalTag = DataMaps.tags.get(original);
		Tag predictedTag = DataMaps.tags.get(predicted);
		HashMap<Tag, Integer> innerMap = new HashMap<Tag, Integer>();
		Integer prevCount = 0;
		// if the confusion matrix already has the innermap for the particular
		// key
		if (confusionMatrix.containsKey(originalTag)) {
			innerMap = confusionMatrix.get(originalTag);
			// if the inner map also contains the entry for predicted map ==>
			// the cell for (actual, predicted) pair is already there
			if (innerMap != null && innerMap.containsKey(predictedTag)) {
				prevCount = innerMap.get(predictedTag);
				// System.out.println("predicted tag"+prevCount);
				innerMap.put(predictedTag, prevCount + 1);
			} else {
				// (actual, predicted) cell not present . add a new entry with
				// count = 1
				innerMap.put(predictedTag, new Integer(1));
			}

		} else {
			// if its a new entry ==> enter count =1
			innerMap.put(predictedTag, new Integer(1));

		}
		confusionMatrix.put(originalTag, innerMap);
	}

	@Override
	/**
	 * this method returns the count from a particular cell in the confusion matrix
	 */
	public int getCount(String original, String predicted) {
		Tag originalTag = new Tag(original);
		Tag predicTag = new Tag(predicted);
		Integer count = 0;
		Map<Tag, Integer> innerMap = confusionMatrix.get(originalTag);
		if (innerMap != null && innerMap.containsKey(predicTag)) {
			count = (Integer) innerMap.get(predicTag);
		}
		return count;
	}

	@Override
	public double getProbability(String row, String col) {
		return 0;
	}

	/**
	 * Method calculating accuracy for each tag
	 */
	/*
	 * public void calculatePerPosAccuracy() {
	 * 
	 * System.out.println("calculation begins"); Integer count = 0; Integer
	 * matchingCount = 0; Double perTagAccuracy = 0.0; Double perTagRecall =
	 * 0.0; Set<Tag> keys = confusionMatrix.keySet(); int tagCount =
	 * keys.size(); int xIndex = 0, yIndex = 0;
	 * 
	 * 
	 * for(Tag curOriginalTag : keys){ // get the map corresponding to the each
	 * original tag HashMap<Tag, Integer> outerMap =
	 * confusionMatrix.get(curOriginalTag); Set<Tag> innerKeySet =
	 * outerMap.keySet(); for(Tag curPredictedTag : innerKeySet){ count +=
	 * outerMap.get(curPredictedTag);
	 * if(curPredictedTag.equals(curOriginalTag)){ matchingCount =
	 * outerMap.get(curPredictedTag); } } perTagAccuracy = new
	 * Double(matchingCount)/new Double(count);
	 * 
	 * System.err.println("pertag accuracy for" + curOriginalTag +
	 * "is:"+perTagAccuracy); }
	 * 
	 * // double[][] confArray = this.convertToMatrix();
	 * 
	 * if(confArray != null){
	 * 
	 * for(Tag tag : keys){
	 * 
	 * Set<Tag> innerKeys = confusionMatrix.keySet(); for (Tag innerTag :
	 * innerKeys){
	 * 
	 * }
	 * 
	 * }
	 * 
	 * }
	 * 
	 * }
	 */

	/**
	 * method will generate a csv file of the confusion matrix
	 * 
	 * @throws IOException
	 */
	public void produceConfMatrix(String filename) throws IOException {
		File file = new File(filename);
		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
				new FileOutputStream(file)));
		// writer.write(builder.toString());
		// StringBuilder builder = new StringBuilder();
		writer.write(",,");
		SortedSet<String> keys = tags;
		for (String tag : keys) {
			// writer.write(String.format("%s,", tag));
			writer.write(tag + ",");
		}
		writer.write("Total\n");

		// Data Rows
		String firstColumnLabel = "Actual Class,";
		for (String outerKey : keys) {
			writer.write(firstColumnLabel);
			firstColumnLabel = ",";
			// writer.write(String.format("%s,", outerKey));
			writer.write(outerKey + ",");
			for (String innerKey : keys) {
				writer.write("" + getCount(outerKey, innerKey));
				writer.write(",");
			}
			// Actual Class Totals Column
			writer.write("" + getTruePosFalseNegCount(outerKey));
			writer.write("\n");
		}

		writer.write("\n");
		writer.close();
		// System.out.println(builder.toString());
		// Desktop.getDesktop().open(file);
	}

	/**
	 * calculates precision recall and fmeasure for each tag/class
	 * 
	 * @throws IOException
	 */
	public void producePosAccuracyFinal(String filename) throws IOException {
		// StringBuilder builder = new StringBuilder();
		File file = new File(filename);
		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
				new FileOutputStream(file)));
		// System.out.println(builder.toString());
		writer.write(",tag,precision,recall,fmeasure\n");
		// builder.append(",tag,precision,recall,fmeasure\n");
		for (String tag : tags) {
			double precision = 0.0;
			double recall = 0.0;
			double fmeasure = 0.0;
			int truePosFalsePosCount = getTruePosFalsePosCount(tag);
			int truePosFalseNegCount = getTruePosFalseNegCount(tag);
			double truePosCount = getTruePosCount(tag);
			//System.out.println("truePosFalsePosCount:" + truePosFalsePosCount + "truePosCount:" + truePosCount);
			if (truePosFalsePosCount != 0)
				precision = truePosCount / truePosFalsePosCount;
			if (truePosFalseNegCount != 0)
				recall = truePosCount / truePosFalseNegCount;
			if (!(precision == 0.0 && recall == 0.0))
				fmeasure = 2 * precision * recall / (precision + recall);
			//System.out.println("tag:" + tag + " precision:" + precision + " recall" + recall + " fmeasure" + fmeasure);
			writer.write("," + String.format("%s", tag) + ","
					+ String.format("%s", precision) + ","
					+ String.format("%s", recall) + ","
					+ String.format("%s", fmeasure) + "\n");
		}
		writer.close();
	}

	// this was a Quick and Dirty code
	/*
	 * public void calculatePerPosAccuracyNew() { HashSet<String> outerKeyList =
	 * tags; System.out.println("size:"+tags.size()); int keyTagCount =
	 * outerKeyList.size()+2; System.out.println("tagcount:"+keyTagCount);
	 * double[][] confMatrix = new double[keyTagCount][keyTagCount]; //this will
	 * store the partial sum for each tag double[] truePosPlusFalsePos = new
	 * double[keyTagCount]; //true predictions + all the tags predicted as
	 * different than the actual tag double[] truePosPlusFalseNeg = new
	 * double[keyTagCount]; double[] truePos = new double[keyTagCount]; int i=0,
	 * j=0; System.out.println("outerlist count:"+outerKeyList.size());
	 * for(String outerKey: outerKeyList){ HashMap< Tag, Integer> innerMap =
	 * confusionMatrix.get(new Tag(outerKey)); if(innerMap != null){ //Set<Tag>
	 * innerKeySet = innerMap.keySet(); HashSet<String> innerKeyList = tags;
	 * for(String innerKey : innerKeyList){
	 * System.out.println("inner key list count:"+innerKeyList.size());
	 * if(innerMap.containsKey(new Tag (innerKey))){
	 * 
	 * int count = innerMap.get(new Tag (innerKey)); confMatrix[i][j]=count; //
	 * ith row corresponding to a particular original tag: jth col correspoding
	 * to a purticular predicted tag if(outerKey.equals(innerKey)){
	 * truePos[i]=count;
	 * 
	 * } truePosPlusFalseNeg[i] +=count;
	 * 
	 * } j++; } } j=0; i++; } //calculate true positive + false positive sum
	 * for( int indexO=0 ; indexO<keyTagCount ; indexO++ ){ for( int indexI=0 ;
	 * indexI < keyTagCount ; indexI++ ){//row ==> actual //col ==> predicted
	 * truePosPlusFalsePos[indexI] += confMatrix[indexO][indexI]; } }
	 * Iterator<String> tagItr = tags.iterator(); int index =0;
	 * while(tagItr.hasNext()){ //how many are correct in total predicted double
	 * precision = truePos[index] / truePosPlusFalsePos[index]; double recall =
	 * truePos[index] / truePosPlusFalseNeg[index]; double fmeasure =
	 * 2*precision*recall /(precision + recall);
	 * System.out.println("Tag:"+tagItr
	 * .next()+" precision:"+precision+" recall:"+recall); index++; } //return
	 * confMatrix;
	 * 
	 * }
	 */
	/**
	 * this will plot the matrix TODO
	 */
	/*
	 * public void showMatrix() {
	 * 
	 * JavaPlot p = new JavaPlot();
	 * 
	 * p.setTitle("per pos tag accuracy");
	 * 
	 * p.addPlot("sin(x)");
	 * 
	 * p.plot();
	 * 
	 * }
	 */

}
